#include "UacOut.h"
#include "UacUtil.h"
#include "log.h"






namespace Usbstream{
UacOut::UacOut(int soundnum){
	m_soundnum = soundnum;
	m_alsainit = false;
	initUacOut();
}

bool UacOut::initUacOut(){
	if(!UacUtil::parseDeviceParam(m_soundnum, 0, m_supportParam)){
		LOG_PrintError("get device params faild soundnum:%d\n", m_soundnum);
	}
	m_used = true;
	return true;
}

UacOut::~UacOut(){
#if 0
	int err;
	if ((err = snd_pcm_close(m_playbackHandle)) < 0) {
		LOG_PrintError("close alsa device faild (%s)\n", snd_strerror(err));
	}
#endif
	stopStream();

	m_supportParam.clear();
	m_soundnum = -1;
	m_used = false;
}


bool UacOut::getSupportParams(Json::Value &params){
	if(m_supportParam.size() == 0){
		LOG_PrintError("cannot get params soundnum:%d\n", m_soundnum);
		return false;
	}
	params = m_supportParam;
	return true;
}

bool UacOut::setParams(const Json::Value params){
	if(m_alsainit){
		return false;
	}
	Json::Value ret;
	Json::Value support;
	Json::Value::Members paramsMember;
	Json::Value::Members supportMember;
	string name = devnamePrefix + to_string(m_soundnum);
	int err;

	if(m_supportParam.size() == 0){
		LOG_PrintError("no support params soundnum:%d\n", m_soundnum);
		return false;
	}

	support  = m_supportParam;

	paramsMember = params.getMemberNames();
	for(auto paramsit = paramsMember.begin(); paramsit != paramsMember.end(); paramsit++){
		if(!support.isMember(*paramsit)){
			LOG_PrintError("cannot support the params %s\n", (*paramsit).c_str());
			return false;
		}
		ret[*paramsit] = params[*paramsit].asString();
	}
	supportMember = support.getMemberNames();
	for(auto supportit = supportMember.begin(); supportit != supportMember.end(); supportit++){
		if(!params.isMember(*supportit)){
			ret[*supportit] = support[*supportit].asString();
		}
	}


	LOG_PrintInfo("UacOut::params json ---> %s\n", ret.toStyledString().c_str());


	if ((err = snd_pcm_open(&m_playbackHandle, name.c_str(), SND_PCM_STREAM_PLAYBACK, 0)) < 0) {
		LOG_PrintError("cannot open audio device %s (%s)\n", name.c_str(), snd_strerror(err));
		return false;
	}

	snd_pcm_hw_params_alloca(&m_hwParams);

	if ((err = snd_pcm_hw_params_any(m_playbackHandle, m_hwParams)) < 0){
		LOG_PrintError("cannot initialize hardware parameter structure (%s)\n", snd_strerror(err));
		return false;
	}

	if ((err = snd_pcm_hw_params_set_access(m_playbackHandle, m_hwParams, SND_PCM_ACCESS_RW_INTERLEAVED)) < 0) {
		LOG_PrintError("cannot set access type (%s)\n", snd_strerror(err));
		return false;
	}


	if ((err = snd_pcm_hw_params_set_format(m_playbackHandle, m_hwParams, SND_PCM_FORMAT_S16_LE)) < 0) {
		LOG_PrintError("cannot set sample format (%s)\n", snd_strerror(err));
		return false;
	}

	unsigned int channel = stoi(ret["Channels"].asString());
	if ((err = snd_pcm_hw_params_set_channels(m_playbackHandle, m_hwParams, channel))  < 0) {
		LOG_PrintError("cannot set channel count (%s)\n", snd_strerror(err));
		return false;
	}

	int dir = SND_PCM_STREAM_PLAYBACK;
#if 1
	unsigned int sample_rate = stoi(ret["Rates"].asString());
	int val = sample_rate / 1000 * 16;
	LOG_PrintInfo("set Rates: %d\n", val);
	err = snd_pcm_hw_params_set_period_size(m_playbackHandle, m_hwParams, val, dir);
	if (err < 0)
	{
		printf("[Func]:%s [Line]:%d info error: %s", __FUNCTION__, __LINE__, snd_strerror(err));
		return false;
	}
#endif

	if ((err = snd_pcm_hw_params_set_rate_near(m_playbackHandle, m_hwParams, &sample_rate, &dir)) < 0) {
		LOG_PrintError("cannot set sample rate (%s)\n", snd_strerror(err));
		return false;
	}

	if ((err = snd_pcm_hw_params(m_playbackHandle, m_hwParams)) < 0) {
		LOG_PrintError("cannot set parameters (%s)\n", snd_strerror(err));
		return false;
	}

	if ((err = snd_pcm_prepare(m_playbackHandle)) < 0) {
		LOG_PrintError("cannot prepare audio interface for use (%s)\n", snd_strerror(err));
		return false;
	}

	m_alsainit = true;
	LOG_PrintError("set uacout succeed\n");

	return true;
}


bool UacOut::writeStream(void *buffer, int length){
	int err;
	//unsigned int channels = 0;
	//snd_pcm_uframes_t frames;
	int bStatusSendFinish = 0;

	if(NULL == buffer)
	{
		printf("%s: pstFrame is null\n", __FUNCTION__);
		return false;
	}

	if (!m_hwParams)
	{
		printf("%s: pUvcCtl_s is null\n", __FUNCTION__);
		return false;
	}

	/* Use a buffer large enough to hold one period */
	//snd_pcm_hw_params_get_period_size(m_hwParams, &frames, &dir);
	//snd_pcm_hw_params_get_channels(m_hwParams, &channels);
	//printf("frames:%lu, channels:%lu\n", frames, channels);
	//size = frames * 2 * channels;

	/* send data to alsa driver */
	while(!bStatusSendFinish)
	{
		
		err = snd_pcm_writei(m_playbackHandle, buffer, length);
		if (err == -EPIPE)
		{
			/* EPIPE means underrun */
			printf("usbstream underrun occurred, err = %d\n", err);
			snd_pcm_prepare(m_playbackHandle);
		}
		else if (err < 0)
		{
			//printf("error from writei: %s\n", snd_strerror(err));
			return false;
		}
		else if (err != length)
		{
			printf("short write, write %d frames\n", err);
			return false;
		}
		else
		{
			bStatusSendFinish = 1;
		}
	}

	return true;
}

bool UacOut::stopStream(){
	if(!m_alsainit){
		return true;
	}
	int err = 0;
	if ((err = snd_pcm_close(m_playbackHandle)) < 0) {
		LOG_PrintError("close alsa device faild (%s)\n", snd_strerror(err));
		return false;
	}
	m_alsainit = false;
	//LOG_PrintInfo("stopStream true");
	return true;
}

}
